#include <QApplication>
#include <QFocusEvent>
#include <QDockWidget>
#include <QStatusBar>
#include <QMessageBox>
#include <QFileDialog>
#include <QToolBar>
#include <Configuration.h>
#include <FileExplorerWindow.h>
#include <WorkspaceExplorerWindow.h>
#include <MessageBrowserWindow.h>
#include <IssuesBrowserWindow.h>
#include <XMLCoreWindow.h>
#include <SettingsDialog.h>
#include <Constants.h>
#include <Resources.h>
#include <DockWidgetInterface.h>
#include <MessageServices.h>
#include "MainWindow.h"
#include "ActionManager.h"

namespace Domy {

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent),
      m_impl(new ActionImpl(this)),
      m_status_bar(new QStatusBar(this)),
      m_file_explorer(Q_NULLPTR),
      m_workspace_explorer(Q_NULLPTR),
      m_msg_browser(Q_NULLPTR),
      m_issues_browser(Q_NULLPTR)
{
    this->setWindowIcon(QIcon(":/logo/logo"));

    this->setWindowTitle(tr("DomyXML v%1").arg(Version));

    auto ctrl = std::bind(&ActionManager::setActionEnable, domy_act, std::placeholders::_1, std::placeholders::_2);

    m_file_explorer = new FileExplorerWindow(ctrl, this);

    m_workspace_explorer = new WorkspaceExplorerWindow(ctrl, this);

    m_msg_browser = new MessageBrowserWindow(ctrl, this);

    m_issues_browser = new IssuesBrowserWindow(ctrl, this);

    m_core_window = new XMLCoreWindow(ctrl, this);

    this->registCentralWidget(m_core_window);

    this->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);

    QDockWidget *dock_msg = m_msg_browser->createDockWidget();
    domy_act->addDockWindowAction(dock_msg->toggleViewAction());
    this->addDockWidget(Qt::BottomDockWidgetArea, dock_msg);

    QDockWidget *dock_issues = m_issues_browser->createDockWidget();
    domy_act->addDockWindowAction(dock_issues->toggleViewAction());
    this->addDockWidget(Qt::BottomDockWidgetArea, dock_issues);

    QDockWidget *dock_workspace = m_workspace_explorer->createDockWidget();
    domy_act->addDockWindowAction(dock_workspace->toggleViewAction());
    this->addDockWidget(Qt::LeftDockWidgetArea, dock_workspace);

    QDockWidget *dock_file = m_file_explorer->createDockWidget();
    domy_act->addDockWindowAction(dock_file->toggleViewAction());
    this->addDockWidget(Qt::LeftDockWidgetArea, dock_file);

    this->tabifyDockWidget(dock_workspace, dock_file);
    dock_workspace->raise();

    this->tabifyDockWidget(dock_msg, dock_issues);
    dock_msg->raise();

    initToolBar();

    domy_act->updateViewMenu();

    m_original_layout = this->saveState();

    this->restoreWindow();

    this->setMenuBar(domy_act->getMenuBar());

    this->setStatusBar(m_status_bar);

    connect(domy_act, SIGNAL(signalActionTriggered(ActionKey, bool)),
            this, SLOT(slotOnActionTriggered(ActionKey, bool)));

    connect(domy_act, SIGNAL(signalRecentFileActionTriggered(const QString &)),
            this, SLOT(slotOnRecentFileActionTriggered(const QString &)));

    connect(m_core_window, SIGNAL(signalNewDocumentFilePathChanged(const QString &)),
            this, SLOT(slotOnNewDocumentFilePathChanged(const QString &)));

    connect(m_workspace_explorer, SIGNAL(signalOpenFile(const QString &)),
            this, SLOT(slotOnOpenFile(const QString &)));

    connect(m_file_explorer, SIGNAL(signalOpenFile(const QString &)),
            this, SLOT(slotOnOpenFile(const QString &)));

    connect(domy_msg, SIGNAL(signalShowStatus(const QString &, int)),
            m_status_bar, SLOT(showMessage(const QString &, int)));
}

MainWindow::~MainWindow()
{
    delete m_core_window;
    m_core_window = Q_NULLPTR;

    delete m_msg_browser;
    m_msg_browser = Q_NULLPTR;

    delete m_file_explorer;
    m_file_explorer = Q_NULLPTR;

    delete m_impl;
    m_impl = Q_NULLPTR;
}

void MainWindow::registCentralWidget(QWidget *widget)
{
    this->setCentralWidget(widget);
}

void MainWindow::saveWindow()
{
    QByteArray state = this->saveState();
    QByteArray geometry = this->saveGeometry();

    domy_config->saveWindowState(state);
    domy_config->saveWindowGeometry(geometry);
}

void MainWindow::restoreWindow()
{
    QByteArray state = domy_config->loadWindowState();
    QByteArray geometry = domy_config->loadWindowGeometry();

    this->restoreState(state);
    this->restoreGeometry(geometry);
}

void MainWindow::loadWindowsLayout()
{
    QString file_name = QFileDialog::getOpenFileName(this, QObject::tr("Load Windows Layout ..."), domy_config->getLastOpenDirPath(), "*.state");
    if (file_name.isEmpty()) {
        return;
    }
    QByteArray data = domy_config->loadWindowState(file_name);
    if (this->restoreState(data)) {
        QMessageBox::information(this, QObject::tr("Load Windows Layout"),
                                 QObject::tr("The windows layout loaded successfully !"),
                                 QMessageBox::Ok, QMessageBox::Ok);
    }
    else {
        QMessageBox::critical(this, QObject::tr("Load Windows Layout"),
                              QObject::tr("The windows layout file(\"%1\") loaded unsuccessfully !").arg(file_name),
                              QMessageBox::Ok, QMessageBox::Ok);
    }
}

void MainWindow::resetWindowsLayout()
{
    if (this->restoreState(m_original_layout)) {
        QMessageBox::information(this, QObject::tr("Reset Windows Layout"),
                                 QObject::tr("The windows layout reset successfully !"),
                                 QMessageBox::Ok, QMessageBox::Ok);
    }
    else {
        QMessageBox::critical(this, QObject::tr("Save As Windows Layout"),
                              QObject::tr("The windows layout reset unsuccessfully !"),
                              QMessageBox::Ok, QMessageBox::Ok);
    }

}

void MainWindow::saveWindowsLayout()
{
    QByteArray data = this->saveState();
    if (domy_config->saveWindowState(data)) {
        QMessageBox::information(this, QObject::tr("Save Windows Layout"),
                                 QObject::tr("The windows layout saved successfully !"),
                                 QMessageBox::Ok, QMessageBox::Ok);
    }
    else {
        QMessageBox::critical(this, QObject::tr("Save Windows Layout"),
                              QObject::tr("The windows layout saved unsuccessfully !"),
                              QMessageBox::Ok, QMessageBox::Ok);
    }
}

void MainWindow::saveAsWindowsLayout()
{
    QString file_name = QFileDialog::getSaveFileName(this, QObject::tr("Save As Windows Layout ..."),
                                                     domy_config->getLastOpenDirPath(), "*.state");
    if (file_name.isEmpty()) {
        return;
    }

    QByteArray data = this->saveState();
    if (domy_config->saveWindowState(data, file_name)) {
        QMessageBox::information(this, QObject::tr("Save As Windows Layout"),
                                 QObject::tr("The windows layout saved successfully !"),
                                 QMessageBox::Ok, QMessageBox::Ok);
    }
    else {

        QMessageBox::critical(this, QObject::tr("Save As Windows Layout"),
                              QObject::tr("The windows layout file(\"%1\") saved unsuccessfully !").arg(file_name),
                              QMessageBox::Ok, QMessageBox::Ok);
    }
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    this->saveWindow();

    domy_config->saveRecetFilesList();

    m_core_window->closeAllDocuments();

    QMainWindow::closeEvent(event);
}

void MainWindow::slotOnActionTriggered(ActionKey key, bool is_checked)
{
    switch (key) {
    case ActKey_NewFile:
        m_impl->newFile();
        break;
    case ActKey_OpenFile:
        m_impl->openFile();
        break;
    case ActKey_CloseFile:
        m_impl->closeFile();
        break;
    case ActKey_CloseAllFiles:
        m_impl->closeAllFiles();
        break;
    case ActKey_RecentFilesClear:
        m_impl->clearRecentFiles();
        break;
    case ActKey_SaveOne:
        m_impl->save();
        break;
    case ActKey_SaveAs:
        m_impl->saveAs();
        break;
    case ActKey_SaveAll:
        m_impl->saveAll();
        break;
    case ActKey_Exit:
        this->close();
        break;
    case ActKey_Undo:
    case ActKey_Redo:
    case ActKey_Copy:
    case ActKey_Cut:
    case ActKey_Paste:
    case ActKey_SelectAll:
    case ActKey_Find:
    case ActKey_FindPrev:
    case ActKey_FindNext:
    case ActKey_ShortcutComment:
    case ActKey_ShortcutApostrophe:
    case ActKey_ShortcutQuotation:
    case ActKey_ShortcutAngleBracket:
    case ActKey_ShortcutParen:
    case ActKey_ShortcutBracket:
    case ActKey_ShortcutBrace:
        m_impl->doAction(key);
        break;
    case ActKey_Settings:
        m_impl->settings();
        break;
    case ActKey_WindowsLayoutLoad:
        this->loadWindowsLayout();
        break;
    case ActKey_WindowsLayoutReset:
        this->resetWindowsLayout();
        break;
    case ActKey_WindowsLayoutSave:
        this->saveWindowsLayout();
        break;
    case ActKey_WindowsLayoutSaveAs:
        this->saveAsWindowsLayout();
        break;
    default:
        break;
    }
}

void MainWindow::slotOnRecentFileActionTriggered(const QString &file_path)
{
    m_impl->openFile(file_path);
}

void MainWindow::slotOnNewDocumentFilePathChanged(const QString &file_path)
{
    QStringList &recent_files = domy_config->getRecentFilesListRef();
    if (recent_files.contains(file_path)) {
        recent_files.removeOne(file_path);
    }
    recent_files.push_front(file_path);

    domy_config->checkRecentFilesCounts();

    domy_act->updateRecentFiles();
}

void MainWindow::slotOnOpenFile(const QString &file_path)
{
    m_impl->openFile(file_path);
}

void MainWindow::initToolBar()
{
    QToolBar *tool_bar = this->addToolBar(tr("File Bar"));
    tool_bar->setObjectName("FileToolBar");
    tool_bar->addAction(domy_act->getAction(ActKey_NewFile));
    tool_bar->addAction(domy_act->getAction(ActKey_OpenFile));
    tool_bar->addSeparator();
    tool_bar->addAction(domy_act->getAction(ActKey_CloseFile));
    tool_bar->addAction(domy_act->getAction(ActKey_CloseAllFiles));
    tool_bar->addSeparator();
    tool_bar->addAction(domy_act->getAction(ActKey_SaveOne));
    tool_bar->addAction(domy_act->getAction(ActKey_SaveAs));
    tool_bar->addAction(domy_act->getAction(ActKey_SaveAll));
    domy_act->addToolBarAction(tool_bar->toggleViewAction());

    tool_bar = this->addToolBar(tr("Edit Bar"));
    tool_bar->setObjectName("EditToolBar");
    tool_bar->addAction(domy_act->getAction(ActKey_Undo));
    tool_bar->addAction(domy_act->getAction(ActKey_Redo));
    tool_bar->addSeparator();
    tool_bar->addAction(domy_act->getAction(ActKey_Copy));
    tool_bar->addAction(domy_act->getAction(ActKey_Cut));
    tool_bar->addAction(domy_act->getAction(ActKey_Paste));
    tool_bar->addSeparator();
    tool_bar->addAction(domy_act->getAction(ActKey_SelectAll));
    tool_bar->addSeparator();
    tool_bar->addAction(domy_act->getAction(ActKey_Find));
    tool_bar->addAction(domy_act->getAction(ActKey_FindPrev));
    tool_bar->addAction(domy_act->getAction(ActKey_FindNext));
    tool_bar->addSeparator();
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutComment));
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutApostrophe));
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutQuotation));
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutAngleBracket));
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutParen));
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutBracket));
    tool_bar->addAction(domy_act->getAction(ActKey_ShortcutBrace));
    domy_act->addToolBarAction(tool_bar->toggleViewAction());

    tool_bar = this->addToolBar(tr("Tools Bar"));
    tool_bar->setObjectName("SettingsToolBar");
    tool_bar->addAction(domy_act->getAction(ActKey_Settings));
    domy_act->addToolBarAction(tool_bar->toggleViewAction());
}

/**
 * @brief ActionImpl
 */
ActionImpl::ActionImpl(MainWindow *main)
    : m_main(main)
{

}

ActionImpl::~ActionImpl()
{

}

void ActionImpl::newFile()
{
    m_main->m_core_window->newDocument();
}

void ActionImpl::openFile()
{
    QString file_path = QFileDialog::getOpenFileName(m_main, QObject::tr("Open File ..."), domy_config->getLastOpenDirPath(), "");
    if (file_path.isEmpty()) {
        return;
    }
    openFile(file_path);
}

void ActionImpl::openFile(const QString &file_path)
{
    if (m_main->m_core_window->openDocument(file_path)) {
        QStringList &recent_files = domy_config->getRecentFilesListRef();
        if (recent_files.contains(file_path)) {
            recent_files.removeOne(file_path);
        }

        recent_files.push_front(file_path);

        domy_config->checkRecentFilesCounts();

        domy_act->updateRecentFiles();

        domy_msg->showStatus(QObject::tr("The file(\"%1\") is opened !").arg(file_path));

    }
}

void ActionImpl::closeFile()
{
    m_main->m_core_window->closeCurrentDocument();
}

void ActionImpl::closeAllFiles()
{
    m_main->m_core_window->closeAllDocuments();
}

void ActionImpl::clearRecentFiles()
{
    QStringList &recent_files = domy_config->getRecentFilesListRef();
    recent_files.clear();

    domy_act->updateRecentFiles();

    domy_msg->showStatus(QObject::tr("The recent files is cleared !"));
}

void ActionImpl::save()
{
    FileSavedReturnCode rslt = m_main->m_core_window->saveCurrentDocument();
    showSaveResultMessage(rslt);
}

void ActionImpl::saveAs()
{
    QString file_path = QFileDialog::getSaveFileName(m_main, QObject::tr("Save As ..."), domy_config->getLastOpenDirPath(), "");
    FileSavedReturnCode rslt = m_main->m_core_window->saveAsCurrentDocument(file_path);
    showSaveResultMessage(rslt);
}

void ActionImpl::saveAll()
{
    m_main->m_core_window->saveAllDocuments();
}

void ActionImpl::doAction(ActionKey key)
{
    QWidget *focus_widget = m_main->focusWidget();
    if (!focus_widget) {
        return;
    }

    if (m_main->m_core_window->isAncestorOf(focus_widget)) {
        m_main->m_core_window->doAction(key);
    }
    else if (m_main->m_file_explorer->isAncestorOf(focus_widget)) {
        m_main->m_file_explorer->doAction(key);
    }
    else if (m_main->m_workspace_explorer->isAncestorOf(focus_widget)) {
        m_main->m_workspace_explorer->doAction(key);
    }
    else if (m_main->m_msg_browser->isAncestorOf(focus_widget)) {
        m_main->m_msg_browser->doAction(key);
    }
    else {

    }
}

void ActionImpl::settings()
{
    SettingsDialog *dialog = new SettingsDialog(m_main);

    QObject::connect(dialog, &SettingsDialog::signalUpdateSettings, [&]() {
        m_main->m_core_window->updateSettings();
    });

    dialog->exec();

    delete dialog;
    dialog = Q_NULLPTR;
}

void ActionImpl::showSaveResultMessage(FileSavedReturnCode rslt)
{
    QString msg;
    switch (rslt) {
    case FileSavedCode_Failed:
        msg = QObject::tr("The file saved unsuccessfully !");
        break;
    case FileSavedCode_Suc:
        msg = QObject::tr("The file saved successfully !");
        break;
    case FileSavedCode_NoFilePath:
        msg = QObject::tr("The file path is wrong !");
        break;
    default:
        break;
    }

    if (!msg.isEmpty()) {
        domy_msg->showStatus(msg);
    }
}

} // namespace Domy
